//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include "CovarianceAttributes.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <fstream>
#include <iostream>
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define MAX_SIZE 500000
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
CovarianceAttributes::CovarianceAttributes () {

  this->filenameAttributes  = new String ();
  this->filenameMatrix      = new String ();
  this->attributes          = new ArffObject ();
  this->covarianceMatrix    = NULL;
  this->toString            = new String ();

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
CovarianceAttributes::CovarianceAttributes (const CovarianceAttributes& covarianceAttributes) {

  this->filenameAttributes  = new String (covarianceAttributes.filenameAttributes);
  this->filenameMatrix      = new String (covarianceAttributes.filenameMatrix);
  this->attributes          = new ArffObject (covarianceAttributes.attributes);
  
  int size = this->attributes->Attributes()->Length() - 2;
  
  if (this->attributes->Attributes()->Length()) {
    
    this->covarianceMatrix = new float* [size];
    for (int i = 0; i < size; i++) {
      this->covarianceMatrix[i] = new float [size];
      for (int j = 0; j < size; j++) 
        this->covarianceMatrix[i][j] = covarianceAttributes.covarianceMatrix[i][j];
    }
    
  }
  else this->covarianceMatrix = NULL;
  
  this->toString            = new String (covarianceAttributes.toString);

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
CovarianceAttributes::CovarianceAttributes (const CovarianceAttributes* covarianceAttributes) {

  this->filenameAttributes  = new String (covarianceAttributes->filenameAttributes);
  this->filenameMatrix      = new String (covarianceAttributes->filenameMatrix);
  this->attributes          = new ArffObject (covarianceAttributes->attributes);

  int size = this->attributes->Attributes()->Length() - 2;
  
  if (this->attributes->Attributes()->Length()) {
    
    this->covarianceMatrix = new float* [size];
    for (int i = 0; i < size; i++) {
      this->covarianceMatrix[i] = new float [size];
      for (int j = 0; j < size; j++) 
        this->covarianceMatrix[i][j] = covarianceAttributes->covarianceMatrix[i][j];
    }
    
  }
  else this->covarianceMatrix = NULL;

  this->toString    = new String (covarianceAttributes->toString);

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
CovarianceAttributes::~CovarianceAttributes () {

  if (this->filenameAttributes) delete this->filenameAttributes;
  if (this->filenameMatrix)     delete this->filenameMatrix;
  
  for (int i = 0; i < this->attributes->Attributes()->Length() - 2; i++)
    if (this->covarianceMatrix[i]) delete [] this->covarianceMatrix[i];
  
  if (this->covarianceMatrix)   delete [] this->covarianceMatrix;
  
  if (this->attributes)         delete this->attributes;
  if (this->toString)           delete this->toString;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void CovarianceAttributes::Attributes (ArffObject* attributes) {

  if (attributes)
    *(this->attributes) = *attributes;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void CovarianceAttributes::FilenameAttributes (String* filenameAttributes) {

  if (filenameAttributes)
    *(this->filenameAttributes) = *filenameAttributes;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void CovarianceAttributes::FilenameMatrix (String* filenameMatrix) {

  if (filenameMatrix)
    *(this->filenameMatrix) = *filenameMatrix;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void CovarianceAttributes::CovarianceMatrix (float** covarianceMatrix, int size) {

  if (covarianceMatrix) {
    
    for (int i = 0; i < this->attributes->Attributes()->Length() - 2; i++)
      if (this->covarianceMatrix[i]) delete [] this->covarianceMatrix[i];

    if (this->covarianceMatrix) delete [] this->covarianceMatrix;
    
    if (size) {
      
      this->covarianceMatrix = new float* [size];
      for (int i = 0; i < size; i++) {
        this->covarianceMatrix[i] = new float [size];
        for (int j = 0; j < size; j++) 
          this->covarianceMatrix[i][j] = covarianceMatrix[i][j];
      }
      
    }
    else this->covarianceMatrix = NULL;
    
  }

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//

ArffObject* CovarianceAttributes::Attributes (void) {

  return this->attributes;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* CovarianceAttributes::FilenameAttributes (void) {

  return this->filenameAttributes;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* CovarianceAttributes::FilenameMatrix (void) {

  return this->filenameMatrix;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
float** CovarianceAttributes::CovarianceMatrix (void) {

  return this->covarianceMatrix;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool CovarianceAttributes::ComputeCovariance (void) {

  if (!this->attributes->Attributes() || this->attributes->Attributes()->Length() < 2 || !this->attributes->Data())
    return false;

  if (this->covarianceMatrix) {

    for (int i = 0; i < this->attributes->Attributes()->Length() - 2; i++)
      if (this->covarianceMatrix[i]) delete [] this->covarianceMatrix[i];

    delete [] this->covarianceMatrix;
    
  }

  int size = this->attributes->Attributes()->Length() - 2;

  if (this->attributes->Attributes()->Length()) {
    
    this->covarianceMatrix = new float* [size];
    for (int i = 0; i < size; i++) {
      this->covarianceMatrix[i] = new float [size];
      for (int j = 0; j < size; j++) 
        this->covarianceMatrix[i][j] = covarianceMatrix[i][j];
    }
    
  }
  else return false;

  for (int i = 0; i < this->attributes->Attributes()->Length() - 2; i++) {
    
    for (int j = i; j < this->attributes->Attributes()->Length() - 2; j++) {
      
      if (i == j) {
        this->covarianceMatrix[i][j] = 0;
        continue;
      }
      
      float xAverage = 0;
      float yAverage = 0;
      
//      cout << this->attributes->Domains()->Length() << endl;
      
      for (int k = 0; k < this->attributes->Domains()->Length(); k++)
        if (this->attributes->Data()[k]) {
          xAverage += this->attributes->Data()[k][i];
          yAverage += this->attributes->Data()[k][j];
//          cout << " - " << this->attributes->Data()[k][i] << " - " << this->attributes->Data()[k][j] << endl;
        }
      
      xAverage /= ((float)this->attributes->Domains()->Length());
      yAverage /= ((float)this->attributes->Domains()->Length());
      
//      cout << "xAverage: " << xAverage << " " << this->attributes->Attributes()->Get(i + 1)->Value()->Out() << endl;
//      cout << "yAverage: " << yAverage << " " << this->attributes->Attributes()->Get(j + 1)->Value()->Out() << endl;
      
      this->covarianceMatrix[i][j] = 0;
      
      for (int k = 0; k < this->attributes->Domains()->Length(); k++)
        this->covarianceMatrix[i][j] += (this->attributes->Data()[k][i] - xAverage) * (this->attributes->Data()[k][j] - yAverage);
      
      this->covarianceMatrix[i][j] /= ((float)this->attributes->Domains()->Length());
      this->covarianceMatrix[j][i] = this->covarianceMatrix[i][j];
      
    }
    
  }

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool CovarianceAttributes::WriteResultMatrix (void) {

  if (!this->filenameAttributes || !this->filenameAttributes->Length() ||
      !this->filenameMatrix     || !this->filenameMatrix->Length())
    return false;

  ofstream fileAttribute (this->filenameAttributes->Out());
  if (!fileAttribute.is_open()) 
    return false;
  
  for (int i = 1; i < this->attributes->Attributes()->Length() - 1; i++)
    fileAttribute << this->attributes->Attributes()->Get(i)->Value()->Out() << endl;
  
  fileAttribute.close();
  
  ofstream fileMatrix (this->filenameMatrix->Out());
  if (!fileMatrix.is_open()) 
    return false;
  
  for (int i = 1; i < this->attributes->Attributes()->Length() - 1; i++) 
    fileMatrix << this->attributes->Attributes()->Get(i)->Value()->Out() << " ";
  fileMatrix << endl;
  
  for (int i = 0; i < this->attributes->Attributes()->Length() - 2; i++) {
    
    for (int j = 0; j < this->attributes->Attributes()->Length() - 2; j++) {
      fileMatrix.width(10);
      fileMatrix.precision(2);
      fileMatrix << scientific << this->covarianceMatrix[i][j];
    }
    
    fileMatrix << endl;
    
  }
  
  fileMatrix.close();
  
  return true;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
CovarianceAttributes CovarianceAttributes::operator= (const CovarianceAttributes& covarianceAttributes) {

  *(this->attributes )        = *(covarianceAttributes.attributes);
  *(this->filenameAttributes) = *(covarianceAttributes.filenameAttributes);
  *(this->filenameMatrix)     = *(covarianceAttributes.filenameMatrix);
  
  for (int i = 0; i < this->attributes->Attributes()->Length() - 2; i++)
    if (this->covarianceMatrix[i]) delete [] this->covarianceMatrix[i];

  if (this->covarianceMatrix) delete [] this->covarianceMatrix;

  int size = covarianceAttributes.attributes->Attributes()->Length() - 2;
  
  if (covarianceAttributes.attributes->Attributes()->Length()) {
    
    this->covarianceMatrix = new float* [size];
    for (int i = 0; i < size; i++) {
      this->covarianceMatrix[i] = new float [size];
      for (int j = 0; j < size; j++) 
        this->covarianceMatrix[i][j] = covarianceMatrix[i][j];
      
    }
  }
  else this->covarianceMatrix = NULL;
  
  *(this->toString   )        = *(covarianceAttributes.toString);

  return *this;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
